package com.archtecture.control.facedes;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;

import com.archtecture.control.enums.TipoOrdenacao;
import com.archtecture.control.exceptions.NegocioException;
import com.archtecture.control.models.ModelAb;
import com.archtecture.control.util.JPQLFactory;

public abstract class PersistenceFacadeImplAbs implements PersistenceFacadeLocal {

	@Override
	public <Model extends ModelAb> Model atualizar(Model pModel) throws NegocioException {
		return getManager().merge(pModel);
	}

	@SuppressWarnings("unchecked")
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public <Model extends ModelAb> Model carregar(Model pModel) throws NegocioException {
		String jpqlString = JPQLFactory.montarConsultaJPQLResultadoUnico(pModel);
		try {
			return (Model) getManager().createQuery(jpqlString).getSingleResult();
		}
		catch (NoResultException e) {
			// throw new
			// NegocioException(Mensagem.ERRO_ENTIDADE_NAO_ENCONTRADA,e);
			return null;
		}
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public <Model extends ModelAb> void excluir(Model pModel) throws NegocioException {
		getManager().remove(getManager().find(pModel.getClass(), pModel.getCodigo()));
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public <Model extends ModelAb> Model inserir(Model pModel) throws NegocioException {
		getManager().persist(pModel);
		return pModel;
	}

	@Override
	public <Model extends ModelAb> List<Model> pesquisarLista(Model pModel, TipoOrdenacao tipoOrdenacao, String... atributosOrdenacao)
			throws NegocioException {

		String jpqlString = JPQLFactory.montarConsultaJPQLMultiplosResultados(pModel, tipoOrdenacao, atributosOrdenacao);
		return executarJPQLResultadoMultiplo(jpqlString);
	}

	@Override
	public <Model extends ModelAb> Model pesquisarUnico(Model pModel, String... pCollectionsName) throws NegocioException {
//		String jpqlString = JPQLFactory.montarConsultaJPQLResultadoUnico(pModel); TODO Verificar esse metodo: pode se chamar carregar
		
		String jpqlString = JPQLFactory.montarConsultaJPQLMultiplosResultados(pModel, TipoOrdenacao.ASC);
		
		Model lModel = executarJPQLResultadoUnico(jpqlString);

		if (pCollectionsName != null) {

			for (String string : pCollectionsName) {
				try {
					Hibernate.initialize(pModel.getClass().getMethod(string, (Class<?>[]) null).invoke(pModel, (Object[]) null));
				}
				catch (HibernateException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
						| NoSuchMethodException | SecurityException e) {
					throw new NegocioException(e);
				}
			}
		}

		return lModel;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <Model> List<Model> executarJPQLResultadoMultiplo(String jpql) throws NegocioException {
		Query query = getManager().createQuery(jpql);
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	@Override
	public <Model> Model executarJPQLResultadoUnico(String jpql) throws NegocioException {
		return (Model) getManager().createQuery(jpql).getSingleResult();
	}

	public abstract EntityManager getManager();
}